package com.jike.shop.cloud.service;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jike.shop.cloud.IUserService;
import com.jike.shop.cloud.entity.User;
import com.jike.shop.cloud.mapper.UserMapper;
import com.jike.shop.cloud.user.dto.Authentication;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author Observer
 */
@DubboService(group = "jike-shop", version = "1.0.0", protocol = "dubbo")
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    /**
     * Token 缓存前缀
     */
    private static final String TOKEN_PREFIX = "token:";

    /**
     * 默认账号
     */
    private static final User DEFAULT_USER;

    static {
        DEFAULT_USER = new User();
        DEFAULT_USER.setId(0);
        DEFAULT_USER.setUsername("jikeshop");
        DEFAULT_USER.setPassword("12345678");
        DEFAULT_USER.setName("极客商城");
        DEFAULT_USER.setCreateTime(System.currentTimeMillis());
        DEFAULT_USER.setModifiedTime(System.currentTimeMillis());
    }

    /**
     * Redis
     */
    @Autowired
    private RedisTemplate<Object, Object> cache;

    @Override
    public Authentication login(String username, String password) {
        User user = findByUsername(username);
        if (Objects.isNull(user) || !user.getPassword().equals(password)) {
            throw new RuntimeException("账号或密码错误");
        }
        AuthenticationManager manager = new AuthenticationManager(user);

        String key = TOKEN_PREFIX + manager.getId();
        cache.opsForValue().set(key, manager, AuthenticationManager.ACCESS_TOKEN_EXPIRES_IN, TimeUnit.MILLISECONDS);
        return manager.create();
    }

    private User findByUsername(String username) {
        if (DEFAULT_USER.getUsername().equals(username)) {
            return DEFAULT_USER;
        }
        return getOne(query().eq("username", username));
    }

    @Override
    // TODO 高频，限流（本地、分布式），队列；活跃用户 Token 有效期规则
    public Authentication refresh(String refreshToken) {
        DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(AuthenticationManager.SIGN_SECRET))
                                   .build()
                                   .verify(refreshToken);
        String tokenId = decodedJWT.getId();

        if (Boolean.FALSE.equals(cache.hasKey(TOKEN_PREFIX + tokenId))) {
            throw new RuntimeException("Token 已过期");
        }
        AuthenticationManager manager = (AuthenticationManager) cache.opsForValue().get(TOKEN_PREFIX + tokenId);
        if (Objects.isNull(manager)) {
            throw new RuntimeException("Token 已过期");
        }

        String key = TOKEN_PREFIX + manager.getId();
        cache.opsForValue().set(key, manager, AuthenticationManager.ACCESS_TOKEN_EXPIRES_IN, TimeUnit.MILLISECONDS);
        return manager.refresh();
    }

}
